/*
 * Copyright 2007-2008 Andrew O'Malley
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package ws.quokka.plugin.help;

import ws.quokka.core.util.Strings;
import ws.quokka.core.util.URLs;
import ws.quokka.core.util.xml.Document;
import ws.quokka.core.util.xml.Element;

import java.net.URL;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;


/**
 *
 */
public class PluginHelpGenerator extends AbstractHelpGenerator {
    //~ Methods --------------------------------------------------------------------------------------------------------

    public String getDescription() {
        Element pluginEl = metaDocument.getRoot();
        Element descriptionEl = pluginEl.getChild("description");

        return (descriptionEl == null) ? null : descriptionEl.getText();
    }

    protected URL getMetadataURL() {
        return URLs.toURL(artifact.getLocalCopy(), plugin.getPluginPath(artifact.getId()));
    }

    protected void addContent(Document out, Element bodyEl) {
        Element pluginEl = metaDocument.getRoot();
        List targets = pluginEl.getChild("targets").getChildren("target");
        List mainTargets = new ArrayList();
        List minorTargets = new ArrayList();
        List abstractTargets = new ArrayList();
        List implementedTargets = new ArrayList();
        List templateTargets = new ArrayList();

        for (Iterator i = targets.iterator(); i.hasNext();) {
            Element targetEl = (Element)i.next();

            if (targetEl.getAttribute("implements") != null) {
                implementedTargets.add(targetEl);
            } else if ("true".equals(targetEl.getAttribute("template"))) {
                templateTargets.add(targetEl);
            } else if ("true".equals(targetEl.getAttribute("abstract"))
                    || ((pluginEl.getAttribute("class") == null) && (targetEl.getAttribute("abstract") == null))) {
                abstractTargets.add(targetEl);
            } else if ("true".equals(targetEl.getAttribute("main"))) {
                mainTargets.add(targetEl);
            } else {
                minorTargets.add(targetEl);
            }
        }

        List all = new ArrayList();
        addTargetsSummary(bodyEl, "Main Targets", mainTargets, all);
        addTargetsSummary(bodyEl, "Minor Targets", minorTargets, all);
        addTargetsSummary(bodyEl, "Abstract Targets", abstractTargets, all);
        addTargetsSummary(bodyEl, "Implemented Targets", implementedTargets, all);
        addTargetsSummary(bodyEl, "Template Targets", templateTargets, all);

        for (Iterator i = all.iterator(); i.hasNext();) {
            Element targetEl = (Element)i.next();
            addTargetDetail(bodyEl, pluginEl, targetEl);
        }
    }

    private void addTargetDetail(Element bodyEl, Element pluginEl, Element targetEl) {
        bodyEl.addChild("hr");

        String name = targetEl.getAttribute("name");
        Element h2el = bodyEl.addChild("h2");
        h2el.addChild("a").setAttribute("name", name).addChild("span");
        h2el.addText(name);

        String description = targetEl.getAttribute("description");

        if (description != null) {
            bodyEl.addChild("p").addText(description);
        }

        Element ulEl = bodyEl.addChild("ul");
        addAttribute(ulEl, targetEl, "abstract");
        addAttribute(ulEl, targetEl, "alias");
        addAttribute(ulEl, targetEl, "depends");
        addAttribute(ulEl, targetEl, "prefix");
        addAttribute(ulEl, targetEl, "template-name");
        addAttribute(ulEl, targetEl, "template");
        addAttribute(ulEl, targetEl, "enabled-by-default");

        if (ulEl.getChildren("li").size() == 0) {
            bodyEl.getElement().removeChild(ulEl.getElement()); // Empty ul not valid xhtml
        }

        String implementsVal = targetEl.getAttribute("implements");

        if (implementsVal != null) {
            Element liEl = ulEl.addChild("li");
            liEl.addChild("b").addText("Implements: ");
            addImplementsRef(liEl, implementsVal);
        }

        // Insert per target content here
        copyContent(new Document(bodyEl.getElement().getOwnerDocument()), bodyEl, name);

        Map defaultProperties = getDefaultProperties(pluginEl, targetEl);

        if (defaultProperties.size() != 0) {
            bodyEl.addChild("b").addText("Default Properties").addChild("br");
            targetEl.getChildren("property");

            Element tableEl = bodyEl.addChild("table").setAttribute("class", "properties");

            for (Iterator i = defaultProperties.entrySet().iterator(); i.hasNext();) {
                Map.Entry entry = (Map.Entry)i.next();
                Element trEl = tableEl.addChild("tr");
                trEl.addChild("td").addText((String)entry.getKey());
                trEl.addChild("td").addText((String)entry.getValue());
            }
        }
    }

    private Map getDefaultProperties(Element pluginEl, Element targetEl) {
        String prefix = targetEl.getAttribute("prefix");
        Map defaults = new TreeMap();

        for (Iterator i = targetEl.getChildren("property").iterator(); i.hasNext();) {
            Element propertyEl = (Element)i.next();
            defaults.put(fixName(prefix, propertyEl.getAttribute("name")), propertyEl.getAttribute("value"));
        }

        Element propertiesEl = pluginEl.getChild("properties");

        if (propertiesEl != null) {
            for (Iterator i = propertiesEl.getChildren("property").iterator(); i.hasNext();) {
                Element propertyEl = (Element)i.next();
                List targets = Strings.commaSepList(propertyEl.getAttribute("targets"));

                if ((targets.size() == 0) || targets.contains(targetEl.getAttribute("name"))) {
                    defaults.put(fixName(prefix, propertyEl.getAttribute("name")), propertyEl.getAttribute("value"));
                }
            }
        }

        return defaults;
    }

    private String fixName(String prefix, String name) {
        if (name.startsWith("prefix.")) {
            return name.substring(7);
        }

        if ((prefix != null) && name.startsWith(prefix)) {
            return name.substring(prefix.length() + 1);
        }

        return name;
    }

    private void addAttribute(Element ulEl, Element targetEl, String attribute) {
        String value = targetEl.getAttribute(attribute);

        if (value != null) {
            Element liEl = ulEl.addChild("li");
            liEl.addChild("b").addText(attribute + ": ");
            liEl.addText(value);
        }
    }

    private void addTargetsSummary(Element outBodyEl, String title, List targets, List all) {
        if (targets.size() == 0) {
            return;
        }

        outBodyEl.addChild("h2").addText(title);

        Element tableEl = outBodyEl.addChild("table");

        for (Iterator i = targets.iterator(); i.hasNext();) {
            Element targetEl = (Element)i.next();
            Element rowEl = tableEl.addChild("tr");
            String name = targetEl.getAttribute("name");
            rowEl.addChild("td").addChild("a").setAttribute("href", "#" + name).addText(name);

            String description = targetEl.getAttribute("description");
            String implementsVal = targetEl.getAttribute("implements");

            if (implementsVal != null) {
                addImplementsRef(rowEl.addChild("td").addText("Implements "), implementsVal);
            } else {
                rowEl.addChild("td").addText((description == null) ? "" : description);
            }
        }

        all.addAll(targets);
    }

    private void addImplementsRef(Element element, String implementsVal) {
        String[] tokens = Strings.split(implementsVal, ":");
        element.addChild("a").setAttribute("href", tokens[0] + ".html#" + tokens[2]).addText(tokens[0] + ":"
            + tokens[2]);
    }
}
